// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "include/libplatform/v8-tracing.h"

#include "base/trace_event/common/trace_event_common.h"
#include "include/v8-platform.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"

namespace v8 {
namespace platform {
    namespace tracing {

        // We perform checks for nullptr strings since it is possible that a string arg
        // value is nullptr.
        V8_INLINE static size_t GetAllocLength(const char* str)
        {
            return str ? strlen(str) + 1 : 0;
        }

        // Copies |*member| into |*buffer|, sets |*member| to point to this new
        // location, and then advances |*buffer| by the amount written.
        V8_INLINE static void CopyTraceObjectParameter(char** buffer,
            const char** member)
        {
            if (*member) {
                size_t length = strlen(*member) + 1;
                strncpy(*buffer, *member, length);
                *member = *buffer;
                *buffer += length;
            }
        }

        void TraceObject::Initialize(
            char phase, const uint8_t* category_enabled_flag, const char* name,
            const char* scope, uint64_t id, uint64_t bind_id, int num_args,
            const char** arg_names, const uint8_t* arg_types,
            const uint64_t* arg_values,
            std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
            unsigned int flags, int64_t timestamp, int64_t cpu_timestamp)
        {
            pid_ = base::OS::GetCurrentProcessId();
            tid_ = base::OS::GetCurrentThreadId();
            phase_ = phase;
            category_enabled_flag_ = category_enabled_flag;
            name_ = name;
            scope_ = scope;
            id_ = id;
            bind_id_ = bind_id;
            flags_ = flags;
            ts_ = timestamp;
            tts_ = cpu_timestamp;
            duration_ = 0;
            cpu_duration_ = 0;

            // Clamp num_args since it may have been set by a third-party library.
            num_args_ = (num_args > kTraceMaxNumArgs) ? kTraceMaxNumArgs : num_args;
            for (int i = 0; i < num_args_; ++i) {
                arg_names_[i] = arg_names[i];
                arg_values_[i].as_uint = arg_values[i];
                arg_types_[i] = arg_types[i];
                if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE)
                    arg_convertables_[i] = std::move(arg_convertables[i]);
            }

            bool copy = !!(flags & TRACE_EVENT_FLAG_COPY);
            // Allocate a long string to fit all string copies.
            size_t alloc_size = 0;
            if (copy) {
                alloc_size += GetAllocLength(name) + GetAllocLength(scope);
                for (int i = 0; i < num_args_; ++i) {
                    alloc_size += GetAllocLength(arg_names_[i]);
                    if (arg_types_[i] == TRACE_VALUE_TYPE_STRING)
                        arg_types_[i] = TRACE_VALUE_TYPE_COPY_STRING;
                }
            }

            bool arg_is_copy[kTraceMaxNumArgs];
            for (int i = 0; i < num_args_; ++i) {
                // We only take a copy of arg_vals if they are of type COPY_STRING.
                arg_is_copy[i] = (arg_types_[i] == TRACE_VALUE_TYPE_COPY_STRING);
                if (arg_is_copy[i])
                    alloc_size += GetAllocLength(arg_values_[i].as_string);
            }

            if (alloc_size) {
                // Since TraceObject can be initialized multiple times, we might need
                // to free old memory.
                delete[] parameter_copy_storage_;
                char* ptr = parameter_copy_storage_ = new char[alloc_size];
                if (copy) {
                    CopyTraceObjectParameter(&ptr, &name_);
                    CopyTraceObjectParameter(&ptr, &scope_);
                    for (int i = 0; i < num_args_; ++i) {
                        CopyTraceObjectParameter(&ptr, &arg_names_[i]);
                    }
                }
                for (int i = 0; i < num_args_; ++i) {
                    if (arg_is_copy[i]) {
                        CopyTraceObjectParameter(&ptr, &arg_values_[i].as_string);
                    }
                }
            }
        }

        TraceObject::~TraceObject() { delete[] parameter_copy_storage_; }

        void TraceObject::UpdateDuration(int64_t timestamp, int64_t cpu_timestamp)
        {
            duration_ = timestamp - ts_;
            cpu_duration_ = cpu_timestamp - tts_;
        }

        void TraceObject::InitializeForTesting(
            char phase, const uint8_t* category_enabled_flag, const char* name,
            const char* scope, uint64_t id, uint64_t bind_id, int num_args,
            const char** arg_names, const uint8_t* arg_types,
            const uint64_t* arg_values,
            std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
            unsigned int flags, int pid, int tid, int64_t ts, int64_t tts,
            uint64_t duration, uint64_t cpu_duration)
        {
            pid_ = pid;
            tid_ = tid;
            phase_ = phase;
            category_enabled_flag_ = category_enabled_flag;
            name_ = name;
            scope_ = scope;
            id_ = id;
            bind_id_ = bind_id;
            num_args_ = num_args;
            flags_ = flags;
            ts_ = ts;
            tts_ = tts;
            duration_ = duration;
            cpu_duration_ = cpu_duration;
        }

    } // namespace tracing
} // namespace platform
} // namespace v8
